package utility;

import gragh.ENode;
import gragh.Redundancy;
import gragh.Way;

import java.util.ArrayList;

import constraint.*;
import constraint.join.Join;

import database.attset.SetAttribute;
import database.fd.FunctionalDependency;
import database.relation.Relation;
import database.relation.Table;

public class Utility {
	/**
	 * lay ra index cua table trong list table
	 * @param tableName
	 * @param listTable
	 * @return index cua table | -1 neu trong list table ko ton tai tableName
	 */
	public static int getIndexOfTable(String tableName, ArrayList<Table> listTable) {
		int index = -1;
		for(int i = 0; i < listTable.size(); i++) {
			Table t = listTable.get(i);
			if(t.getTableName().equalsIgnoreCase(tableName)) {
				index = i;
			}
		}
		return index;
	}
	
	/**
	 * lay ra index cua relatioName trong list relation
	 * @param relationName
	 * @param listRelation
	 * @return index cua relationName | -1 neu trong list relation ko ton tai relationName
	 */
	public static int getIndexOfRelation(String relationName, ArrayList<Relation> listRelation) {
		int index = -1;
		for(int i = 0; i < listRelation.size(); i++) {
			Relation r = listRelation.get(i);
			if(r.getRname().equalsIgnoreCase(relationName)) {
				index = i;
			}
		}
		return index;
	}
	
	/**
	 * lay ra index cua attribute trong listRelation.
	 * @param tableName
	 * @param attribute
	 * @param listTable
	 * @param listRelation
	 * @return index cua attribute | -1 neu ko ton tai
	 */
	public static int getIndexAttributeInTable(String tableName, String attribute, ArrayList<Table> listTable, ArrayList<Relation> listRelation) {
		int index = -1;
		int indexT = getIndexOfTable(tableName, listTable);
		if (indexT != -1) {
    		Relation r = listRelation.get(indexT);
    		String[] listAttribute = listTable.get(indexT).getListColumn();
    		for(int i = 0; i < listAttribute.length; i++) {
    			if(listAttribute[i].equalsIgnoreCase(attribute)) {
    				index = r.get(i);
    			}
    		}
		}
		return index;
	}
	
	public static String getAttributeInTable(int index, ArrayList<Table> listTable, ArrayList<Relation> listRelation) {
		String result = "";
		for(int i = 0; i < listRelation.size(); i++) {
			Relation r = listRelation.get(i);
			int _indexR = r.indexOf(new Integer(index));
			if(_indexR != -1) {
				Table t = listTable.get(i);
				result = t.getListColumn()[_indexR];
			}
		}
		
//		int i = 0;
//		boolean found = false;
//		while (!found && i < listRelation.size()) {
//		    Relation r = listRelation.get(i);
//		    if (index <= r.get(r.size()-1)) {
//		        found = true;
//	            int _indexR = r.indexOf(new Integer(index));
//	            Table t = listTable.get(i);
//                result = t.getListColumn()[_indexR];
//		    }
//		    i++;
//		}
		
		
		return result;
	}
	
	public static String getTableNameFormAttIndex(int index, ArrayList<Relation> listRelation) {
	    String result = "";

	    int i = 0;
        boolean found = false;
        while (!found && i < listRelation.size()) {
            Relation r = listRelation.get(i);
            if (index <= r.get(r.size() - 1)) {
                found = true;
                result = r.getRname();
            }
            i++;
        }
	    
	    return result;
	}
	
	public static int getTableIndexFormAttIndex(int index, ArrayList<Relation> listRelation) {
        int result = -1;

        int i = 0;
        boolean found = false;
        while (!found && i < listRelation.size()) {
            Relation r = listRelation.get(i);
            if (index <= r.get(r.size() - 1)) {
                found = true;
                result = i;
            }
            i++;
        }
        
        return result;
    }
	
	public static SetAttribute convertArrayListIntToSetAtt(ArrayList<Integer> lsInt) {
	    SetAttribute set = new SetAttribute();
	    for (Integer inte : lsInt) {
	        set.add(inte);
	    }
	    
	    return set;
	}
	
	public static ArrayList<Integer> convertSetAttToArrayListInt(SetAttribute set) {
	    ArrayList<Integer> lsInt = new ArrayList<Integer>();
	    for (Integer s : set) {
	        lsInt.add(s);
	    }
	    
	    return lsInt;
	}
	
	public static String nodeToString (ENode node, ArrayList<Table> lsT, ArrayList<Relation> lsR) {
	    StringBuffer sb = new StringBuffer();
	    
	    switch (node.type) {
	    case 90:
	        sb.append(node.constant);
	        break;
	    case 20:
	        sb.append("(");
	        
	        for (int i = 0; i < node.size(); i++) {
	            Integer inte = node.get(i);
	            if (i > 0) {
	                sb.append(", ");
	            }
	            sb.append(lsT.get(inte).getTableName());
	        }
	        
	        sb.append(")");
	        break;
	    default:
	        sb.append("(");
            
            for (int i = 0; i < node.size(); i++) {
                Integer index = node.get(i);
                if (i > 0) {
                    sb.append(", ");
                }
                sb.append(Utility.getTableNameFormAttIndex(index, lsR));
                sb.append(".");
                sb.append(Utility.getAttributeInTable(index, lsT, lsR));
            }
            
            sb.append(")");
	        break;
	    }
	    
	    // ket qua ra la: (3 loai)
        // "fgfdgsdg" la hang so
	    // "(A, B, C, E)" la cac bang
	    // "(A.f, G.e)" la cac thuoc tinh
	    return sb.toString();
	}
	
	public static String strShowRedundancy(Redundancy r, ArrayList<Table> lsT, ArrayList<Relation> lsR) {
	    String returnStr = "";
	    ENode n1 = null;
	    ENode n2 = null;
	    

	    if (r.special == 1) {
	        n1 = r.node_1;
            n2 = r.node_2;
	        
	        returnStr = "Các thuộc tính " + Utility.nodeToString(n1, lsT, lsR) + " và các thuộc tính " + Utility.nodeToString(n2, lsT, lsR);
            returnStr += " là dư thừa khi kết theo tham chiếu đệ quy";
	    } else {
    	    if (r.node_1.type == 10 && r.node_1.func.get(0).equals("")) {
    	        n1 = r.node_1;
                n2 = r.node_2;
    	    } else {
    	        n1 = r.node_2;
                n2 = r.node_1;
    	    }
    
    	    returnStr = "Thuộc tính " + Utility.nodeToString(n1, lsT, lsR);
    	    if (n2.type == 90) {
    	        returnStr += " là dư thừa do luôn bằng với hằng số " + Utility.nodeToString(n2, lsT, lsR);
    	    } else if (n2.type == 20) {
    	        returnStr += " sẽ là thuộc tính dư thừa do dẫn xuất khi kết hợp với các bảng " + Utility.nodeToString(n2, lsT, lsR);
    	    } else if (n2.type == 11) {
    	        returnStr += " sẽ là thuộc tính dư thừa do dẫn xuất khi kết hợp với các thuộc tính " + Utility.nodeToString(n2, lsT, lsR);
    	    } else if (n2.type == 10) {
    	        if (r.node_1.func.get(0).equals("")) {
    	            returnStr += " và thuộc tính " + Utility.nodeToString(n2, lsT, lsR) + " là dư thừa do nhân bản";
    	        } else {
    	            returnStr += " là dư thừa do nhân bản với một phần của thuộc tính " + Utility.nodeToString(n2, lsT, lsR); 
    	        }
    	    }
	    }	    
	    returnStr += ".\n";

	    return returnStr;
	}
	
	public static String wayToString(Way w,  ArrayList<Table> lsT, ArrayList<Relation> lsR) {
	    String result = "";
	    
	    String tb1 = lsT.get(w.table1).getTableName();
	    String tb2 = lsT.get(w.table2).getTableName();
	    String att1 = "";
	    String att2 = "";
	            
	    result = tb1 + " JOIN " + tb2 + " ON ";
	    
	    if (w.atts1.size() == 1) {
	        att1 = Utility.getAttributeInTable(w.atts1.get(0), lsT, lsR);
	    } else {
	        att1 = "(";
	        att1 += Utility.getAttributeInTable(w.atts1.get(0), lsT, lsR);
    	    for (int i = 1; i < w.atts1.size(); i++) {
    	        att1 += ", " + Utility.getAttributeInTable(w.atts1.get(i), lsT, lsR);
    	    }
    	    att1 += ")";
	    }
	    
	    if (w.atts2.size() == 1) {
            att2 = Utility.getAttributeInTable(w.atts2.get(0), lsT, lsR);
            
            result += tb1 + "." + att1 + " = " + tb2 + "." + att2;
        } else {
            att2 = "(";
            att2 += Utility.getAttributeInTable(w.atts2.get(0), lsT, lsR);
            for (int i = 1; i < w.atts2.size(); i++) {
                att2 += ", " + Utility.getAttributeInTable(w.atts2.get(i), lsT, lsR);
            }
            att2 += ")";
            
            result += tb1 + att1 + " = " + tb2 + att2;
        }
	    
	    // ket qua ra la: (2 loai)
	    // "Table1 JOIN Table2 AS Table1.att1 = Table2.att2"
	    // "Table1 JOIN Table2 AS Table1(att1, att3) = Table2(att2, att4)"
	    return result;
	}
	
	public static String getListTableNameFromListPathIndex(SetAttribute lsp, ArrayList<Way> theWay, ArrayList<Relation> lsR) {
	    StringBuffer sb = new StringBuffer();
	    
	    SetAttribute lst = new SetAttribute();
	    
	    if (lsp.size() == 1) {
	        lst.add(lsp.get(0));
	    } else {
    	    for (int i = 1; i < lsp.size(); i++) {
    	        Integer indexW = lsp.get(i);
    	        Way w = theWay.get(indexW);
    	        if (!lst.contains(w.table1)) {
    	            lst.add(w.table1);
    	        }
    	        if (!lst.contains(w.table2)) {
    	            lst.add(w.table2);
    	        }
    	    }
	    }
	    
	    sb.append("(");
	    for (int i = 0; i < lst.size(); i++) {
	        Integer tb = lst.get(i);
	        if (i > 0) {
	            sb.append(", ");
	        }
	        sb.append(lsR.get(tb).getRname());
	    }
	    sb.append(")");
	    
	    return sb.toString();
	}
	
	public static String getTitleR(Redundancy r) {
	    StringBuffer sb = new StringBuffer();
	    
	    sb.append(r.node_1.strViewValue);
	    sb.append(" = ");
	    sb.append(r.node_2.strViewValue);
	    
	    return sb.toString();
	}
	
	public static String getEvidence(Redundancy r) {
	    StringBuffer sb = new StringBuffer();
	    boolean fin = false;
	    boolean found_1 = false;
	    boolean found_2 = false;
	    ArrayList<ENode> lsNode_1 = new ArrayList<ENode>();
	    ArrayList<ENode> lsNode_2 = new ArrayList<ENode>();
	    ArrayList<ENode> lsResult = null;
	    
	    lsNode_1.add(r.node_1);
	    lsNode_2.add(r.node_2);
	    
	    fin = false;
	    while (!found_1 && !fin) {
	        ENode curNode = lsNode_1.get(lsNode_1.size()-1);
	        ENode tmpNode = curNode.directLink;
	        if (tmpNode.equals(r.node_2)) {
	            found_1 = true;
	        } else {
	            if (!lsNode_1.contains(tmpNode)) {
	                lsNode_1.add(tmpNode);
	            } else {
	                fin = true;
	            }
	        }
	    }
	    
	    if (found_1) {
	        lsResult = lsNode_1;
	        lsResult.add(r.node_2);
	    } else {
	        int indexMatchNode = 0;
	        
	        fin = false;
	        while (!found_2 && !fin) {
	            ENode curNode = lsNode_2.get(lsNode_2.size()-1);
	            ENode tmpNode = curNode.directLink;
	            if (tmpNode.equals(r.node_1)) {
	                found_2 = true;
	            } else {
	                indexMatchNode = lsNode_1.indexOf(tmpNode);
	                if (indexMatchNode == -1) {
	                    lsNode_2.add(tmpNode);
	                } else {
	                    fin = true;
	                }
	            }
	        }
	        
	        if (found_2) {
	            lsResult = lsNode_2;
	            lsResult.add(r.node_1);
	        } else {
	            lsResult = lsNode_2;
	            for (int i = indexMatchNode; i >= 0; i--) {
	                lsResult.add(lsNode_1.get(i));
	            }
	        }
	    }
	    
	    // chuyen qua string
	    sb.append(lsResult.get(0).strViewValue);
        sb.append(" = ");
        sb.append(lsResult.get(lsResult.size()-1).strViewValue);
	    if (lsResult.size() > 2) {
            sb.append("\nBắc cầu:");
            
            for (int i = 0; i < lsResult.size()-1; i++) {
                sb.append("\n");
                sb.append(lsResult.get(i).strViewValue);
                sb.append(" = ");
                sb.append(lsResult.get(i + 1).strViewValue);
            }
	    }
	    
	    return sb.toString();
	}
	
	/**
	 * Chi dung cho constraint.Constraint_v4
	 * @return
	 */
	public static String joinToString(Constraint_v4 c4, ArrayList<Table> lsT, ArrayList<Relation> lsR) {
	    StringBuffer sb = new StringBuffer();
	    
	    ArrayList<Join> lsJoin = c4.join;
	    String table1 = lsJoin.get(0).table1;
	    String table2 = lsJoin.get(0).table2;
	    
	    sb.append(table1);
        sb.append(" JOIN ");
        sb.append(table2);
        sb.append(" AS ");
	    if (lsJoin.size() == 1) {
	        sb.append(lsJoin.get(0).left);
	        sb.append(" = ");
	        sb.append(lsJoin.get(0).right);
	    } else {
	        String atts_1 = "(";
	        String atts_2 = "(";
	        
	        for (int i = 0; i < lsJoin.size(); i++) {
	            Join join = lsJoin.get(i);
	            if (i > 0) {
	                atts_1 += ", ";
	                atts_2 += ", ";
	            }
	            if (join.table1.equals(table1)) {
	                atts_1 += Utility.getAttributeInTable(join.indexAtt1, lsT, lsR);
	                atts_2 += Utility.getAttributeInTable(join.indexAtt2, lsT, lsR);
	            } else {
	                atts_1 += Utility.getAttributeInTable(join.indexAtt2, lsT, lsR);
                    atts_2 += Utility.getAttributeInTable(join.indexAtt1, lsT, lsR);
	            }
	        }
	        atts_1 += ")";
	        atts_2 += ")";
	        
	        sb.append(atts_1);
            sb.append(" = ");
            sb.append(atts_2);
	    }
	    
	    return sb.toString();
	}
	
	public static ENode getListIndexTableFromListIndexAtt(ENode lsAtt, ArrayList<Relation> lsR) {
	    ENode lsTb = new ENode();
	    
	    for (Integer att : lsAtt) {
	        int tb = Utility.getTableIndexFormAttIndex(att, lsR);
	        if (!lsTb.contains(tb)) {
	            lsTb.add(tb);
	        }
	    }
	    
	    return lsTb;
	}
	
	public static String getAllDetailTable(Table tb, Relation rel, ArrayList<Table> lsT, ArrayList<Relation> lsR) {
        StringBuffer sb = new StringBuffer();
        String[] lsColumn = tb.getListColumn();
        
        sb.append(getAllTable(tb));
        
        sb.append("\nTập khóa: {");
        ArrayList<SetAttribute> lsKeys = rel.getKeys();
        for (int i = 0; i < lsKeys.size(); i++) {
            SetAttribute key = lsKeys.get(i);
            if (i > 0) {
                sb.append(", ");
            }
            
            StringBuffer tmp_sb = new StringBuffer();
            tmp_sb.append("(");
            for (int j = 0; j < key.size(); j++) {
                Integer att = key.get(j);
                if (j > 0) {
                    tmp_sb.append(", ");
                }
                int indexInTb = rel.indexOf(att);
                tmp_sb.append(lsColumn[indexInTb]);
            }
            tmp_sb.append(")");
            
            sb.append(tmp_sb.toString());
        }
        sb.append("}\n-------\nTập phụ thuộc hàm:\n");
        for (FunctionalDependency fd : rel.getListFd()) {
            sb.append(Utility.convertFDtoString(fd, lsT, lsR));
            sb.append("\n");
        }
                
        return sb.toString();
    }
	
	public static String convertFDtoString(FunctionalDependency fd, ArrayList<Table> lsT, ArrayList<Relation> lsR) {
	    StringBuffer sb = new StringBuffer();
	    String left = "";
	    String right = "";
	    
	    left = "(";
        for (int i = 0; i < fd.left.size(); i++) {
            Integer att = fd.left.get(i);
            if (i > 0) {
                left += ", ";
            }
            left += Utility.getAttributeInTable(att, lsT, lsR);
        }
        left += ")";

        right = "(";
        for (int i = 0; i < fd.right.size(); i++) {
            Integer att = fd.right.get(i);
            if (i > 0) {
                right += ", ";
            }
            right += Utility.getAttributeInTable(att, lsT, lsR);
        }
        right += ")";

        sb.append(left + "->" + right);
	    
	    return sb.toString();
	}
	
	public static String getAllTable(Table tb) {
	    StringBuffer sb = new StringBuffer();
	    String[] lsAtt = tb.getListColumn();
	    
	    sb.append(tb.getTableName() + "(");
	    
	    for (int i = 0; i < lsAtt.length; i++) {
	        if (i > 0) {
	            sb.append(", ");
	        }
	        sb.append(lsAtt[i]);
	    }
	    
	    sb.append(")");
	    
	    return sb.toString();
	}
	
	public static String getAllTables(Redundancy r, ArrayList<Table> lsT, ArrayList<Relation> lsR) {
	    StringBuffer sb = new StringBuffer();
	    
	    ENode n1 = r.node_1;
	    ENode n2 = r.node_2;
	    
   
	    if (n1.type == 90) {
	        n1 = new ENode();
	    } else if (n1.type == 20) {
	        // do nothing
	    } else {
	        n1 = Utility.getListIndexTableFromListIndexAtt(n1, lsR);
	    }
	    

	    if (n2.type == 90) {
            n2 = new ENode();
        } else if (n2.type == 20) {
            // do nothing
        } else {
            n2 = Utility.getListIndexTableFromListIndexAtt(n2, lsR);
        }

	    
	    for (Integer in : n2) {
	        if (!n1.contains(in)) {
	            n1.add(in);
	        }
	    }
	    
	    for (Integer indexTb : n1) {
            Table t = lsT.get(indexTb);
            sb.append(Utility.getAllTable(t));
            sb.append("\n");
        }
	    
	    return sb.toString();
	}
}
